package main

import (
	"archive/zip"
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"inter/tools"
	"inter/xuperchain"
	"io"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/dgrijalva/jwt-go"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

type PublicKeyRequest struct {
	Username string `json:"username"`
}

type LoginStruct struct {
	Username string `json:"username" binding:"required"`
	Password string `json:"password" binding:"required"`
}

type User struct {
	gorm.Model
	Username string `json:"username" gorm:"unique"`
	Password string `json:"password"`
}
type UpPublicKey struct {
	gorm.Model
	Username  string `json:"username" gorm:"unique"`
	PublicKey []byte `gorm:"type:blob"`
}

type File struct {
	gorm.Model
	FileIdentifier string `gorm:"primaryKey;unique;not null"`
	Content        []byte `gorm:"type:mediumblob"`
}

type Paper struct {
	ID       int    `json:"id"`
	Title    string `json:"title"`
	Abstract string `json:"abstract"`
}

type PaperRequest struct {
	PaperName string `json:"paperName"`
	Page      int    `json:"page"`
}

var jwtKey = []byte("your_secret_key")

func main() {
	dsn := "root:xxxx@tcp(127.0.0.1:3306)/xuperchain?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("failed to connect database")
	}

	db.AutoMigrate(&File{})
	db.AutoMigrate(&User{})
	db.AutoMigrate(&User{})

	r := gin.Default()
	r.Use(cors.New(cors.Config{
		AllowAllOrigins:  true,
		AllowMethods:     []string{"GET", "POST", "OPTIONS", "PUT", "DELETE"},
		AllowHeaders:     []string{"Origin", "Content-Type", "Accept", "Authorization"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		MaxAge:           12 * time.Hour,
	}))

	r.POST("/api/login", func(c *gin.Context) {
		var loginInfo LoginStruct

		if err := c.ShouldBindJSON(&loginInfo); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		// 从数据库中查找用户
		var user User
		if err := db.Where("username = ?", loginInfo.Username).First(&user).Error; err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
			return
		}

		// 验证密码
		if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(loginInfo.Password)); err != nil {
			c.JSON(http.StatusUnauthorized, gin.H{"error": "用户名或密码错误"})
			return
		}

		// 如果验证通过，生成 JWT token
		token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
			"username": loginInfo.Username,
			"nbf":      time.Now().Unix(),
			"exp":      time.Now().Add(time.Hour * 1).Unix(), // token 有效期为1小时
		})

		tokenString, err := token.SignedString(jwtKey)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法生成令牌"})
			return
		}

		// 返回带有JWT token的成功响应
		c.JSON(http.StatusOK, gin.H{"token": tokenString})
	})

	r.POST("/api/register", func(c *gin.Context) {
		var newUser User
		if err := c.ShouldBindJSON(&newUser); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		// 检查username是否唯一
		var existingUser User
		if err := db.Where("username = ?", newUser.Username).First(&existingUser).Error; err == nil {
			c.JSON(http.StatusConflict, gin.H{"error": "用户名已被使用"})
			return
		}

		// 对密码进行哈希处理
		hashedPassword, err := bcrypt.GenerateFromPassword([]byte(newUser.Password), bcrypt.DefaultCost)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法加密密码"})
			return
		}
		newUser.Password = string(hashedPassword)

		// 存入数据库
		result := db.Create(&newUser)
		if result.Error != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "注册失败"})
			return
		}

		// 返回注册成功的消息
		c.JSON(http.StatusOK, gin.H{"message": "注册成功"})
	})

	r.POST("/api/upload", tools.AuthMiddleware(), func(c *gin.Context) {
		//上传信息到区块链
		username := c.MustGet("username").(string)
		paperName := c.PostForm("paperName")
		abstract := c.PostForm("abstract")
		users := c.PostForm("users")

		if paperName == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "请输入文件名字"})
			return
		}
		form, _ := c.MultipartForm()
		files := form.File["files"]

		// 遍历解压后的文件并根据文件名解析出所需信息
		for _, fileHeader := range files {
			baseFileName := filepath.Base(fileHeader.Filename)
			fileIdentifierWithoutExtension := strings.TrimSuffix(baseFileName, ".dat")

			// 解析文件名 "id_文件名_权限_序号.dat"
			parts := strings.Split(fileIdentifierWithoutExtension, "_")
			if len(parts) < 4 {
				c.JSON(http.StatusBadRequest, gin.H{"error": "无效的文件命名格式"})
				return
			}

			// 比较文件名中的paperName部分是否与前端传来的paperName一致
			if parts[1] != paperName {
				c.JSON(http.StatusBadRequest, gin.H{"error": "文件名与上传的文件不匹配"})
				return
			}
			file, err := fileHeader.Open()
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "无法打开文件"})
				return
			}
			defer file.Close()

			// 读取文件内容
			content, err := io.ReadAll(file)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "读取文件失败"})
				return
			}

			// 保存到数据库
			if err := tools.SaveFileToDB(db, fileIdentifierWithoutExtension, content); err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "保存到数据库失败"})
				return
			}
		}
		if xuperchain.FileStorage(username, paperName, abstract, users) != "success" {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "文件上传失败"})
			return
		}

		c.JSON(http.StatusOK, gin.H{"message": "文件上传成功"})
	})

	r.POST("/api/download/:fileIdentifier", tools.AuthMiddleware(), func(c *gin.Context) {
		username := c.MustGet("username").(string)
		fileIdentifier := c.Param("fileIdentifier")

		if xuperchain.IsUserAuthorized(fileIdentifier, username) == "false" {
			c.JSON(http.StatusForbidden, gin.H{"error": "您没有权限！"})
			return
		}
		Level := xuperchain.GetUserPermissionLevel(fileIdentifier, username)

		var file1, file2 File
		result := db.Where("file_identifier = ?", username+"_"+fileIdentifier+"_"+Level+"_1").First(&file1)
		if result.Error != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "文件1获取失败"})
			return
		}
		result = db.Where("file_identifier = ?", username+"_"+fileIdentifier+"_"+Level+"_2").First(&file2)
		if result.Error != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "文件2获取失败"})
			return
		}

		file1Content := []byte(file1.Content)
		sign1, err := tools.GenerateSign(file1Content)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "签名1生成失败"})
			return
		}

		fmt.Printf("%x\n", file2.Content)
		file3Content, err := tools.ReEncryptAsn1(file2.Content)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "重加密失败"})
			return
		}
		sign3, err := tools.GenerateSign(file3Content)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "签名3生成失败"})
			return
		}

		publicFilePath := "tools/sm2Key/PublicFile.dat"
		publicFileContent, err := os.ReadFile(publicFilePath)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "公钥文件获取失败"})
			return
		}

		// 添加查看记录
		xuperchain.AddViewRecord(fileIdentifier, username)

		// 创建压缩文件
		buf := new(bytes.Buffer)
		zipWriter := zip.NewWriter(buf)

		f1, _ := zipWriter.Create("file1.dat")
		_, _ = f1.Write(file1Content)

		f3, _ := zipWriter.Create("file3.dat")
		_, _ = f3.Write(file3Content)

		fsign1, _ := zipWriter.Create("sign1.dat")
		encoder1 := gob.NewEncoder(fsign1)
		err = encoder1.Encode(sign1)
		if err != nil {
			log.Fatal(err)
		}

		fsign3, _ := zipWriter.Create("sign3.dat")
		encoder3 := gob.NewEncoder(fsign3)
		err = encoder3.Encode(sign3)
		if err != nil {
			log.Fatal(err)
		}

		fp, _ := zipWriter.Create("PublicFile.dat")
		_, _ = fp.Write(publicFileContent)

		zipWriter.Close()

		c.Header("Content-Disposition", "attachment; filename=files.zip")
		c.Data(http.StatusOK, "application/zip", buf.Bytes())
	})

	r.GET("/api/papers", func(c *gin.Context) {
		page, err := strconv.Atoi(c.DefaultQuery("page", "1"))
		if err != nil || page < 1 {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无效页码"})
			return
		}

		pageSize := 5

		// 获取文件列表长度
		totalFiles := xuperchain.GetFileListLength()
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "获取失败"})
			return
		}

		totalPages := (totalFiles + pageSize - 1) / pageSize
		if page > totalPages {
			c.JSON(http.StatusOK, gin.H{"papers": []Paper{}, "currentPage": page, "totalPages": totalPages})
			return
		}

		paperNames, abstracts := xuperchain.GetAllFiles(page, pageSize)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
			return
		}

		var papers []Paper
		for i := range paperNames {
			papers = append(papers, Paper{
				ID:       i + 1 + (page-1)*pageSize,
				Title:    paperNames[i],
				Abstract: abstracts[i],
			})
		}
		c.JSON(http.StatusOK, gin.H{"papers": papers, "currentPage": page, "totalPages": totalPages})
	})

	r.POST("/api/record", tools.AuthMiddleware(), func(c *gin.Context) {
		username := c.MustGet("username").(string)
		var request struct {
			PaperName string `json:"paperName"`
			Page      int    `json:"page"`
		}

		if err := c.BindJSON(&request); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无效的请求格式"})
			return
		}
		if xuperchain.HasPaper(request.PaperName) == "false" {
			c.JSON(http.StatusForbidden, gin.H{"error": "文件不存在"})
			return
		}
		pageSize := 5
		totalLength, userIDs, times, err := xuperchain.GetViewUserIds(request.PaperName, username, request.Page, pageSize)
		if err != nil {
			c.JSON(http.StatusForbidden, gin.H{"error": "您不是文件所有者"})
			return
		}

		totalPages := (totalLength + pageSize - 1) / pageSize

		records := make([]map[string]interface{}, len(userIDs))
		for i, userID := range userIDs {
			records[i] = map[string]interface{}{
				"id":         userID,
				"accessTime": times[i],
			}
		}

		c.JSON(http.StatusOK, gin.H{
			"records":    records,
			"totalPages": totalPages,
		})
	})

	r.POST("/api/uploadPublicKey", tools.AuthMiddleware(), func(c *gin.Context) {
		username := c.MustGet("username").(string)

		// 读取文件
		csr, err := c.FormFile("CSR")
		sign, err := c.FormFile("sign")
		publicFile, err := c.FormFile("PublicFile")

		if err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": "无法获取上传的文件"})
			return
		}

		// 保存文件
		csrPath := filepath.Join("uploads", csr.Filename)
		signPath := filepath.Join("uploads", sign.Filename)
		publicFilePath := filepath.Join("uploads", publicFile.Filename)

		if err := c.SaveUploadedFile(csr, csrPath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存CSR文件"})
			return
		}

		if err := c.SaveUploadedFile(sign, signPath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存签名文件"})
			return
		}

		if err := c.SaveUploadedFile(publicFile, publicFilePath); err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存公钥文件"})
			return
		}

		// 调用 VerifyCSR 函数
		if tools.VerifyCSR(csrPath, signPath, publicFilePath) {
			publicFileContent, err := os.ReadFile(publicFilePath)
			if err != nil {
				c.JSON(http.StatusInternalServerError, gin.H{"error": "读取公钥文件失败"})
				return
			}

			// 更新数据库中的用户公钥
			upPublicKey := UpPublicKey{
				Username:  username,
				PublicKey: publicFileContent,
			}

			result := db.Create(&upPublicKey)
			fmt.Print(result)
			c.JSON(http.StatusOK, gin.H{"message": "上传成功"})
		} else {
			c.JSON(http.StatusOK, gin.H{"message": "上传失败"})
		}

	})

	r.GET("/api/search", func(c *gin.Context) {

		term := c.Query("term")
		if term == "" {
			c.JSON(http.StatusBadRequest, gin.H{"error": "搜索词不能为空"})
			return
		}

		// 这个函数返回文件的名称和摘要
		name, abstract, err := xuperchain.GetFile(term)
		if err != nil {
			c.JSON(http.StatusNotFound, gin.H{"error": "文件未找到"})
			return
		}

		c.JSON(http.StatusOK, gin.H{
			"papers": []Paper{{Title: name, Abstract: abstract}},
		})
	})

	r.POST("/api/getPublickey", func(c *gin.Context) {
		var request PublicKeyRequest
		if err := c.BindJSON(&request); err != nil {
			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
			return
		}

		var user UpPublicKey
		result := db.Where("username = ?", request.Username).First(&user)
		if result.Error != nil {
			if errors.Is(result.Error, gorm.ErrRecordNotFound) {
				c.JSON(http.StatusOK, gin.H{"message": "用户不存在或公钥未上传"})
				return
			}
			c.JSON(http.StatusInternalServerError, gin.H{"error": result.Error.Error()})
			return
		}

		// 保存公钥到文件
		publicKeyPath := "./publickey/publickey.dat"
		err := os.WriteFile(publicKeyPath, []byte(user.PublicKey), 0644)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "无法保存公钥文件"})
			return
		}

		// 处理公钥文件
		resolvedKey := tools.ResolvePublickey(publicKeyPath)

		// 返回处理后的公钥
		c.JSON(http.StatusOK, gin.H{"resolvedKey": resolvedKey})
	})
	r.GET("/api/downloadtool", func(c *gin.Context) {
		filePath := filepath.Join("./toolkit", "toolkit.zip")
		c.File(filePath)
	})
	r.Run(":8081")
}
